<?php

namespace app\open\controller;

use app\common\model\UrmAliveCustomerGroupQR;
use app\common\variable\MsgCode;
use think\cache\driver\Redis;
use think\facade\Log;
use think\facade\Request;
use weworkapi\WXBizMsgCrypt;
use think\App;
use think\Exception;
use app\common\model\UrmWorkchatCallbackEvent as UrmWorkchatCallbackEventModel;
use think\facade\View;

class WeWorkChat extends Base
{



    protected $_callbackErrorMsgArr = [    //企业微信(添加/删除外部联系人)回调事件错误码+错误信息
        '0' => 'success',
        '-40001' => '签名验证错误',
        '-40002' => 'xml解析失败',
        '-40003' => 'sha加密生成签名失败',
        '-40004' => 'encodingAesKey 非法',
        '-40005' => 'corpid 校验错误',
        '-40006' => 'aes 加密失败',
        '-40007' => 'aes 解密失败',
        '-40008' => '解密后得到的buffer非法',
        '-40009' => 'base64加密失败',
        '-40010' => 'base64解密失败',
        '-40011' => '生成xml失败',
    ];

    private $workCpt;
    public function __construct()
    {
        $token = config('work_wechat.work_wechat_token');
        $encodingAesKey = config('work_wechat.work_wechat_encodingaeskey');
        $corpId = config('work_wechat.corpid');
        $this->workCpt = new WXBizMsgCrypt($token, $encodingAesKey, $corpId);
    }

    /**
     * 企业微信回调
     *
     * @Author: hhm <huanghemao@xycms.com>
     * @DateTime: 2022/3/11 0011 上午 11:08
     * @return string
     */
    public function callbackEvent()
    {
        $params = Request::instance()->param(); //get参数
        if (empty($params)) {
            $ip = request()->ip();
            Log::error("无参请求，来源IP:{$ip}\n");
            Log::save();
            return  $this->ajaxReturn("请求被拒绝", MsgCode::$error);
        }
        $params['xmlContent'] = file_get_contents('php://input');  //post的xml数据
        Log::error("进入回调\n" . json_encode($params));
        Log::save();
        //若回调url验证通过,处理回调的xml消息
        if (!empty($params['xmlContent']) && empty($params['echostr'])) {
            return $this->dealCallbackEvent($params);
        } else {     //验证url有效性
            return $this->validation($params);;  // 解密后的消息内容(务必原文输出),否则报错
        }
    }


    /**
     * 处理URL验证外的所有回调分配
     *
     * @Author: hhm <huanghemao@xycms.com>
     * @DateTime: 2022/3/11 0011 上午 11:08
     * @param $data
     * @return mixed
     * @throws \Exception
     */
    private function dealCallbackEvent($data)
    {
        $sReqMsgSig = $data['msg_signature'];
        $sReqTimeStamp = $data['timestamp'];
        $sReqNonce = $data['nonce'];
        $sReqData = $data['xmlContent'];    // post请求的密文数据
        $decryptMsg = "";  // 解析之后的明文
        $errCode = $this->workCpt->DecryptMsg($sReqMsgSig, $sReqTimeStamp, $sReqNonce, $sReqData, $decryptMsg);
        if ($errCode !== 0) {
            Log::error("errCode:{$errCode},errMsg:{$this->_callbackErrorMsgArr[$errCode]}\n");
            Log::save();
            throw new \Exception("errCode:{$errCode},errMsg:{$this->_callbackErrorMsgArr[$errCode]}");
        }
        $decryptMsgArr = $this->XMLString2Array($decryptMsg);
        Log::error("回调类型" . json_encode($decryptMsgArr) . "\n");
        Log::save();
        switch ($decryptMsgArr['ChangeType']) {
            case 'add_external_contact':    // 添加外部联系人回调事件
                $dealRes = $this->addExternalCallbackEvent($decryptMsgArr);
                break;
            case 'del_follow_user':    // 删除外部联系人回调事件
                $dealRes = $this->delFollowUserCallbackEvent($decryptMsgArr);
                break;
            default:
                Log::error("回调事件类型:{$decryptMsgArr['ChangeType']}\n" . json_encode($data));
                Log::save();
                throw new \Exception($decryptMsgArr['ChangeType'] . '回调事件类型不合法');
        }
        return $this->ajaxReturn("ok", MsgCode::$success);
    }


    /**
     * 企业验证URL函数
     *
     * @Author: hhm <huanghemao@xycms.com>
     * @DateTime: 2022/3/10 0010 下午 17:40
     * @return string
     */
    private function validation($D)
    {
        $sVerifyMsgSig = $D['msg_signature'];
        $sVerifyTimeStamp = $D['timestamp'];
        $sVerifyNonce = $D['nonce'];
        $sVerifyEchoStr = $D['echostr'];
        $sEchoStr = '';
        $errCode = $this->workCpt->VerifyURL($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sVerifyEchoStr, $sEchoStr);
        if ($errCode == 0) {
            return $sEchoStr;
        } else {
            Log::error("解密微信推送的消息失败，错误码为：{$errCode},\n错误码参考链接：https://developer.work.weixin.qq.com/document/path/95372#%E5%8A%A0%E8%A7%A3%E5%AF%86%E5%BA%93%E8%BF%94%E5%9B%9E%E7%A0%81\n数据：" . json_encode($D));
            Log::save();
            throw new Exception("解密微信推送的消息失败，错误码为：{$errCode}");
        }
    }



    /**
     * 提取xml数据中的指定参数
     * (转换原理:xml字符串->xml对象->json对象->数组)
     * @param string xml格式数据
     * @return array array格式数据
     */
    public function XMLString2Array($xmlStr)
    {
        try {
            //xml字符转换为xml对象
            $xmlObj = simplexml_load_string($xmlStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $jsonObj = json_encode($xmlObj);
            return json_decode($jsonObj, true);
        } catch (\Exception $ex) {
            throw new Exception($ex->getMessage());
        }
    }

    /**
     * 添加企业联系人的回调具体处理方法
     *
     * @Author: hhm <huanghemao@xycms.com>
     * @DateTime: 2022/3/11 0011 上午 11:11
     * @param $data
     * @return bool
     * @throws Exception
     */
    private function addExternalCallbackEvent($data)
    {
        $redisConfig = config('cache.stores.redis');
        $redis = new Redis($redisConfig);
        $ret = $redis->lpush("{$redisConfig['prefix']}workCallback-add_half_external_contact", json_encode($data, JSON_UNESCAPED_UNICODE));
        $da = $this->saveEventData($data);
        try {
            UrmWorkchatCallbackEventModel::create($da);
            return true;
        } catch (\Exception $e) {
            Log::error("企业微信回调添加企业联系人的回调具体处理方法\n" . json_encode($data, JSON_UNESCAPED_UNICODE) . "\n");
            Log::save();
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 企业微信回调删除跟进联系人的回调具体处理方法
     *
     * @Author: hhm <huanghemao@xycms.com>
     * @DateTime: 2022/3/16 0016 下午 17:56
     * @param $data
     * @return bool
     * @throws Exception
     */
    private function delFollowUserCallbackEvent($data)
    {
        $redisConfig = config('cache.stores.redis');
        $redis = new Redis($redisConfig);
        $ret = $redis->lpush("{$redisConfig['prefix']}workCallback-del_follow_user", json_encode($data, JSON_UNESCAPED_UNICODE));
        $da = $this->saveEventData($data);
        try {
            UrmWorkchatCallbackEventModel::create($da);
            return true;
        } catch (\Exception $e) {
            Log::error("企业微信回调删除跟进联系人的回调具体处理方法\n" . json_encode($data, JSON_UNESCAPED_UNICODE) . "\n");
            Log::save();
            throw new Exception($e->getMessage());
        }
    }

    /**
     * 将回调事件写入数据库中
     * 
     * @Author: hhm <huanghemao@xycms.com>
     * @DateTime: 2022/3/16 0016 下午 17:57
     * @param $data
     * @return array
     */
    private function saveEventData($data)
    {
        $da = [
            'ToUserName' => $data['ToUserName'],
            'FromUserName' => $data['FromUserName'],
            'create_time' => $data['CreateTime'],
            'MsgType' => $data['MsgType'],
            'Event' => $data['Event'],
            'ChangeType' => $data['ChangeType'] ?? null,
            'params' => json_encode($data),
        ];
        return $da;
    }
}
